package com.rh.file.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rh.entity.file.RenHaiFileDetails;
import com.rh.file.controller.FileParam;
import com.rh.file.service.FileService;
import com.rh.file.service.RenHaiFileDetailsService;
import com.rh.file.service.RenHaiFileService;
import com.rh.file.util.FileUpload;
import com.rh.file.util.FileUtil;
import com.rh.file.util.SftpUtil;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;

/**
 * 文件上传服务层
 */
@Service
public class FileServiceImpl implements FileService {

    private final RenHaiFileService service;
    private final RenHaiFileDetailsService fileDetailsService;

    private final FileUpload fileUpload;

    public FileServiceImpl(RenHaiFileService service, RenHaiFileDetailsService fileDetailsService, FileUpload fileUpload) {
        this.service = service;
        this.fileDetailsService = fileDetailsService;
        this.fileUpload = fileUpload;
    }

    /**
     * 分块上传
     * 第一步：获取RandomAccessFile,随机访问文件类的对象
     * 第二步：调用RandomAccessFile的getChannel()方法，打开文件通道 FileChannel
     * 第三步：获取当前是第几个分块，计算文件的最后偏移量
     * 第四步：获取当前文件分块的字节数组，用于获取文件字节长度
     * 第五步：使用文件通道FileChannel类的 map（）方法创建直接字节缓冲器  MappedByteBuffer
     * 第六步：将分块的字节数组放入到当前位置的缓冲区内  mappedByteBuffer.put(byte[] b);
     * 第七步：释放缓冲区
     * 第八步：检查文件是否全部完成上传
     *
     * @param param
     * @return
     * @throws IOException
     */
    @Override
    public boolean chunkUploadByMappedByteBuffer(FileParam param) throws IOException {
        /**
         * 1：原文件名改为UUID
         * 2：创建临时文件，和源文件一个路径
         * 3：如果文件路径不存在重新创建
         */
        String fileName = param.getFile().getOriginalFilename();//文件mingc

        fileName = param.getTaskId() + fileName.substring(fileName.lastIndexOf("."));

        String tempFileName = param.getTaskId() + fileName.substring(fileName.lastIndexOf(".")) + "_tmp";
        //上传路径
        String filePath = fileUpload.getPath() + "/" + param.getPath();

        File tempFile = null;
        boolean flag = true;
        //ftp
        if(fileUpload.isFtp()){

        }
        //sftp
        else if(fileUpload.isSftp()){
            /* SFTPUtil sftpUtil = new SFTPUtil(fileUpload.getUser(),fileUpload.getPwd(),fileUpload.getUrl(),22); */
            Map<String, String> sftpDetails = new HashMap<String, String>();
            sftpDetails.put(SftpUtil.SFTP_REQ_HOST, "106.13.166.80");
            sftpDetails.put(SftpUtil.SFTP_REQ_USERNAME, "wdsftp");
            sftpDetails.put(SftpUtil.SFTP_REQ_PASSWORD, "wangdao@980609");
            sftpDetails.put(SftpUtil.SFTP_REQ_PORT, "22");
            //测试文件上传
            // 目标文件名
            String dst = "/usr/wenjian/wdsftp/upload/"+fileName;
            try {
                SftpUtil.upload(param,dst,sftpDetails);
            } catch (Exception e) {
                flag = false;
                e.printStackTrace();
            }
            return flag;
        }else{
            File fileDir = new File(filePath);
            if (!fileDir.exists()) {
                fileDir.mkdirs();
            }
             tempFile = new File(filePath, tempFileName);
            //第一步 “rw”：以读、写方式打开，支持文件的读取或写入。若文件不存在，则创建之。
            RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
            //第二步
            FileChannel fileChannel = raf.getChannel();
            //第三步  文件大小
            long offset = (param.getChunkNumber()-1) * param.getChunkSize();
            //第四步
            byte[] fileData = param.getFile().getBytes();
            //第五步
            MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, offset, fileData.length);
            //第六步
            mappedByteBuffer.put(fileData);
            //第七步
            FileUtil.freedMappedByteBuffer(mappedByteBuffer);
            fileChannel.close();
            raf.close();
                QueryWrapper<RenHaiFileDetails> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda()
                        .eq(RenHaiFileDetails::getRenHaiFile, param.getTaskId())
                        .eq(RenHaiFileDetails::getChunkNumber, param.getChunkNumber())
                        .eq(RenHaiFileDetails::getTotalChunks,param.getTotalChunks());
                RenHaiFileDetails fileDetails = fileDetailsService.getOne(queryWrapper);
                if(fileDetails==null){
                    //添加一条分片添加成功记录
                    fileDetailsService.insert(param);
                }
                //第八步
                boolean isComplete = checkUploadStatus(param, fileName, filePath);
                if (isComplete) {
                    //上传成功
                    if(tempFile!=null){
                        renameFile(tempFile, fileName);
                    }
                    service.updateComplete(param.getTaskId(),filePath+"/"+fileName);
                    return true;
                }
        }
        return false;
    }

    /**
     * 文件重命名
     *
     * @param toBeRenamed   将要修改名字的文件
     * @param toFileNewName 新的名字
     * @return
     */
    public boolean renameFile(File toBeRenamed, String toFileNewName) {
        //检查要重命名的文件是否存在，是否是文件
        if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {
            return false;
        }
        String p = toBeRenamed.getParent();
        File newFile = new File(p + File.separatorChar + toFileNewName);
        //修改文件名
        return toBeRenamed.renameTo(newFile);
    }


    /**
     * 检查文件上传进度 本地
     *
     * @return
     */
    public boolean checkUploadStatus(FileParam param, String fileName, String filePath) throws IOException {
        File confFile = new File(filePath, fileName + ".conf");
        RandomAccessFile confAccessFile = new RandomAccessFile(confFile, "rw");
        //设置文件长度
        confAccessFile.setLength(param.getTotalChunks());
        //设置起始偏移量
        confAccessFile.seek(param.getChunkNumber() - 1);
        //将指定的一个字节写入文件中 127，
        confAccessFile.write(Byte.MAX_VALUE);
        //关闭流
        confAccessFile.close();
        byte[] completeStatusList = FileUtils.readFileToByteArray(confFile);
        byte isComplete = Byte.MAX_VALUE;
        //这一段逻辑有点复杂，看的时候思考了好久，创建conf文件文件长度为总分片数，每上传一个分块即向conf文件中写入一个127，那么没上传的位置就是默认的0,已上传的就是Byte.MAX_VALUE 127
        for (int i = 0; i < completeStatusList.length && isComplete == Byte.MAX_VALUE; i++) {
            // 按位与运算，将&两边的数转为二进制进行比较，有一个为0结果为0，全为1结果为1  eg.3&5  即 0000 0011 & 0000 0101 = 0000 0001   因此，3&5的值得1。
            isComplete = (byte) (isComplete & completeStatusList[i]);
        }
        if (isComplete == Byte.MAX_VALUE) {
            //如果全部文件上传完成，删除conf文件file.getAbsoluteFile().delete();
            boolean delete = confFile.getAbsoluteFile().delete();
            while (!delete) {
                delete = confFile.getAbsoluteFile().delete();
            }
            return true;
        }
        return false;
    }
}
